延續昨天主題,今天接著介紹錯誤處理。
昨天介紹了日誌系統,今天來介紹與它息息相關的錯誤處理。
進度圖跟昨天一樣 XD
其實 discord.py
已經幫忙處理很多錯誤了,只不過,有時候我們還是會希望可以稍微插手一下,讓 Discord BOT 可以呈現一些錯誤提示;或者是使用昨天介紹的日誌系統,把需要的資訊紀錄都記錄下來。
接下來,會盡量把前面幾種不同的功能的錯誤處理都展示一下。
Client
的 event
import discord
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
@client.event
async def on_message(message: discord.Message):
if message.author == client.user:
return
if message.content.startswith("ping"):
a = 1 / 0 # 這邊會出錯
await message.channel.send("Pong!")
@client.event
async def on_error(event, *args, **kwargs):
print(f"發生錯誤: {event} {args} {kwargs}")
if event == "on_message":
await args[0].channel.send(f"{event} 發生錯誤了!")
bot.run('token')
當對事件的處理發生錯誤時,就可以被 on_error
捕捉到。雖然可以知道是哪一個事件觸發的,但詳細的錯誤訊息需要另外用 sys.exc_info()
才能拿到了。
Bot
的 command
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='>', intents=intents)
@bot.command()
async def ping(ctx):
a = 1 / 0 # 這邊會出錯
await ctx.send('pong')
# catch command error
@bot.event
async def on_command_error(ctx: commands.Context, error: commands.CommandError):
await ctx.send(f"發生錯誤了: {error}")
bot.run('token')
Bot
可以用自己的 on_command_error
捕捉 bot.command
內的錯誤,不用跟 on_error
擠在一起。
CommandTree
內的 command這邊用 Slash Command 作為例子
# ...
@bot.tree.command()
async def ping(interaction: discord.Interaction):
a = 1 / 0 # 這邊會出錯
await interaction.response.send_message("Pong!")
@bot.tree.error
async def on_error(interaction: discord.Interaction, error: Exception) -> None:
await interaction.response.send_message(f"Oops! {error}", ephemeral=True)
# ...
當 CommandTree
這個容器內的應用指令發生錯誤時,可以用 on_error
來捕捉錯誤。如果需要知道是哪個應用指令出錯,可以用 discord.Interaction.command
取得。
# ...
async def on_click(interaction: discord.Interaction):
a = 1 / 0 # 這邊會出錯
await interaction.response.send_message("剛剛點擊了按鈕")
async def on_btn_error(
interaction: discord.Interaction, error: Exception, item: discord.ui.Item
) -> None:
await interaction.response.send_message(f"Oops! {error}", ephemeral=True)
@bot.command()
async def ping(ctx: commands.Context):
view = discord.ui.View()
btn = discord.ui.Button(label="我是按鈕")
btn.callback = on_click
view.add_item(btn)
view.on_error = on_btn_error
await ctx.send("點擊下方按鈕", view=view)
# ...
如果要捕捉 View
內的錯誤,可以用 View.on_error
來設定。有需要的話,可以從 Item
來取得發生錯誤的元件的相關資訊。
# ...
class Questionnaire(discord.ui.Modal, title="Questionnaire Response"):
name = discord.ui.TextInput(label='Name')
answer = discord.ui.TextInput(label='Answer', style=discord.TextStyle.paragraph)
async def on_submit(self, interaction: discord.Interaction):
a = 1 / 0 # 這邊會出錯
await interaction.response.send_message(
f"Thanks for your response, {self.name}!", ephemeral=True
)
async def on_error(self, interaction: discord.Interaction, error: Exception) -> None:
await interaction.response.send_message(f"Oops! {error}", ephemeral=True)
# ...
如果在 on_submit
發生錯誤,則會被 on_error
捕捉到。由於 Modal 一定要用 subclass 的方式,所以就直接把 on_error
放在 class 之中就好了。同時,這樣管理這些程式碼也可以提高可維護性。
import discord
import logging
from discord.ext import commands, tasks
intents = discord.Intents.default()
intents.message_content = True
bot = commands.Bot(command_prefix='', intents=intents)
logger = logging.getLogger("discord")
@bot.command()
async def ping(ctx):
slow_count.start()
await ctx.send('pong')
@tasks.loop(seconds=1.0, count=5)
async def slow_count():
a = 1 / (3 - slow_count.current_loop) # 第 4 次會出錯
await ctx.send(slow_count.current_loop)
@slow_count.after_loop
async def after_slow_count():
logger.info('done!')
@slow_count.error
async def on_slow_count_error(error: Exception):
logger.error(f"{error}")
bot.run('token')
Discord BOT 在執行第 4 次的時候發生錯誤,導致只有傳送 0 到 2,後面剩餘的次數就沒繼續執行了。
雖然 task 中斷了,但是 after_loop
的內容依然有執行 (紀錄 Log)。而且,發生錯誤的那次,有被 error
捕捉到。
今天介紹了各種情境下要如何去捕捉錯誤,之後不論是要像這篇文章一樣,讓 Discord BOT 把錯誤訊息回傳出來;或是使用昨天介紹的日誌系統,把這些錯誤訊息記錄下來,都是可以的做法。